Object-Oriented Programming Introduction

It is expected that the reader will gain very introductory knowledge of the basis, vocabulary and concepts of Object-Oriented Programming. More in-depth and broader vocabulary, conceptual understanding and techniques as well as evaluation of Object-Oriented Programming strengths and weaknesses are deferred to a later topic.

OOP Introduction

Object-oriented programming is the current state in the evolution of programming styles. Learning Object-Oriented programming involves more than just learning the syntax of an O-O language. Though O-O programming is an evolutionary step from procedural programming, it is not a straight forward, direct extension. Object-Oriented programming involves learning how to think in an Object-Oriented fashion. Realizing that O-O programming encompasses analysis, design and implementation in a more disciplined structure than with previous programming styles develops this thinking. The evolution of programming up to the procedural stage has involved solving problems with artificial abstract programming concepts. These concepts do not relate well to the problem domain being solved. A background in procedural programming can be a hindrance to thinking in an O-O fashion. O-O thinking involves analyzing, designing and implementing solutions to real-world problems in software in a way that is more directly related to the problem. The programming constructs are less artificial but do require more discipline to use. With procedural programming you could get away with just "coding" the solution without much analysis and design. Object-Oriented programming does not afford this lack of discipline. If you do not incorporate O-O analysis and design into the programming process then you will not have an O-O program. You will have a procedural program in an O-O language. This gives you none of the O-O benefits, all the procedural shortcomings and is harder than if done with a procedural language.

Real-world example of design failure:

Story of Vasa. Taken from Object Lessons, Tom Love, SIGS Books, 1993.

Parallels between Shipbuilding in the 1600's and Software in the 1990's

Implications

1. Systems may last longer than we think (sometimes hundreds of years).
2. Maintenance costs may exceed the original development cost.

Real-world example of O-O: Home Stereo System

Power Plant: A "black box" that provides a service. Specifies that outlets must accept 3 wires at 60Hz of A.C. electrical power.

Outlet: Implements a dual interface. Seems like a power plant.
Conforms to the power plant outlet specification.
Provides an interface for a 120v, polarized plug of A.C. electrical power with standard physical dimensions.
The outlet can be implemented in different colors, décor, material, wall fastening system and physical wire orientation into the outlet.
The complex details here illustrate the concept of hiding the complexities behind a simple interface.

Plug: Conforms to an interface.
If the plug is polarized then it conforms to the outlet interface exactly.
If the plug is not polarized then it is compatible with the interface but not an exact fit.

Amp/Tuner, CD, Tape, Speakers: A set of components put together to provide a stereo system to play sound recordings.
These components form an association of loosely coupled items that have a cohesive unity of purpose.

Tape: Assembled with a case, controls, a motor and a read/write head.
These parts are put together to form a whole.

Stereo Interface: Provides to homeowner with exactly what they need.
Components easily and simply connect together. The power plug works only one way.
The interface is not complicated by a control for adjusting the sampling rate of the CD laser for different sound qualities.

"Play" button: Each component that can "play" sound knows how to do this with the medium that it corresponds to.
You can add a turntable and it will know how to play sounds and the rest of the system does not have to change.

Earphones: Has characteristics of speakers but extends the functionality.
Can limit the sound output to one person.

Programming Evolution

Goal

The goal of evolving the task of programming is to achieve better software by a faster process. During the evolution this has been accomplished by trying to match the programming languages with the real-world problem domain. Creating ever-higher levels of abstraction from the artificial world of the computer to the real world does this. A benefit of this abstraction is the emergence of code reuse. Code reuse provides a mechanism to save time (during the development process), utilize "proven" software and facilitates changes. All of this comes at the cost of more discipline. Object-Oriented programming is the next step to do these things in order to reach the goal of better software developed faster.

History

Machine code

Machine code programming was done by using binary digit to code the addresses, instructions and operands of the particular computer system. This process was tedious, error-prone and the program size was limited. The programmer had to work in the world of the computer (binary numbers).

Assembler/Machine language

Assembly languages and linkers were perhaps the first tools used to abstract features of the bare machine. Addresses, instructions and operands could be represented symbolically, not as a number. The linking of symbolic names to locations or numeric representation was performed automatically. Assembly languages permitted the programmer to think more in the human-oriented world of symbolic names and operations.

Procedures and Functions

Procedures and functions provided a mechanism for sharing knowledge across several projects, a way to carry a software component from one program to another. Libraries of procedures and functions gave the first hint to information hiding. Procedures and functions continued the trend started with assembly language of permitting the programmer to think about operations in higher level terms, concentrating on the description of the task to be performed, the what, and subordinating the details of how it is being performed. Procedures as an information hiding mechanism are fine for tasks that have a single simple interface, but are not an entirely effective mechanism for more complex tasks.

Block Scoping

Consider, for example, trying to create a general purpose Stack data structure. Of all components, data structures would seem to be the most obvious candidates for development as a reusable abstraction. The problem is that the stack abstraction has an interface that consists of several procedures. Because the data is shared between each of these procedures, it cannot be local to any of them. It must therefore be global to the procedures. But in a language that has only local and global data (such as FORTRAN or C) the internal data fields, which you would like to hide, must be exposed for the world to see (and perhaps, to modify). One early experiment was the idea of block scoping, which permitted functions to be arbitrarily nested one within the other. Block scoping appears in the language ALGOL 60, an ancestor of Simula, Pascal, and many other languages. One might think that block scoping would provide a better mechanism for information hiding, however this turns out to not be true. If a data area is shared by two or more procedures, it must still be declared in a more general scope than the procedures, and thus be even more exposed than the procedures will be. The language C broke with the general trend towards block scoping, returning to the idea of only two levels of scope (global and local).

Modules

A module is basically a mechanism that allows the programmer to encapsulate a collection of procedures and data, and supply both import and export statements to control the visibility of features from outside the module. The module solved the problem of encapsulation. We could export the data structure access routines (push, pop and the like), while hiding the internal representation. But, what would happen if your programming task required two or more instances of the same type of object, for example two or more stacks? Modules by themselves did not provide any solution to this problem.

Abstract Data Types

The key to solving the problem of needing more than one instance of a software abstraction was provided by the idea of an Abstract Data Type. An ADT is simply a programmer-defined data type that can be manipulated in a manner similar to system provided data types. So, in particular, this implied both the information hiding abilities of the module concept as well the notion that the programmer could create many different instances, or copies, of the new data type. Thus, for example, one could create a stack ADT, and write programs that manipulated multiple stacks.

Next Step O-O

Building upon the history of program language evolution the next step to emerge is Object-Oriented programming. The shortcoming of previous programming innovations to reach the goal of software was combined with several concepts beyond the world of computers. This development is even indicative of the characteristic of O-O to more closely match the real-world problem domain. The philosophical basis for O-O is drawn from the following concepts.

Sapir – Whorf hypothesis

There are some curious parallels between the artificial languages we use in writing computer programs, and the natural languages people use in their everyday life. Let us explore one of these parallels. In linguistics there is a hypothesis that the language in which an idea or thought is expressed colors the nature of the thought. Two American linguists, Edward Sapir and Benjamin Lee Whorf developed this notion in the early part of this century. The example that is often cited is the supposed fact that Eskimo or Innuit languages have many different words for snow. Thus, when an Eskimo looks out on a vast plain of white, they must make a much more detailed differentiation than most people would. Since to even say the simplest thing they need to determine whether the snow is wet, or powdery, or the right kind for making igloos, or whatever. Most people, on the other hand, can simply dismiss it as "snow". A similar argument can be made concerning the plethora of words found in Arabic languages to describe camels. Now there is an obvious conclusion one can make, and a much more controversial one. The obvious conclusion is that languages (both natural and computer) can lead you in one direction or another, but don't preclude you from thinking any thought. Certainly an Eskimo eye is no different from mine, and if I studied it long enough and felt it important enough, I could make the same distinctions as the Eskimo, even if I was speaking only English. Sapir and Whorf went further, and claimed that there were thoughts one could have in one language that could not ever occur, could not even be explained, to somebody thinking in a different language. This stronger form is what is known as the Sapir-Whoft hypothesis, and remains controversial. It is interesting to examine both of these forms in the area of artificial computer languages.

Medium is the Message

Marshall McLuhan’s proposal that electronic media, especially television, were creating a global village in which the medium is the message, i.e., the means of communications has a greater influence on people than the information itself. This idea extended to programming means that the approach and language you use has a great influence over the solution that will be found.

For example, consider that following problem. A strand of DNA can be thought of as simply a very long sequence of four elements, which could be represented by the letters A, C, T and G. ACTCGGATCTTGCATTTCGGCAATTGGACCCTGACTTGGCCA ...

The task is to see if any sequence of M values are repeated. Here N, the number of elements in the DNA sequence, is hugely long but M, the size of the repeated sequence, was very small. To solve the problem as simple, very short and direct (FORTRAN style) program is used. This program is simply the obvious triply nested loop, where the two outer loops control the starting point for a comparison search, and the third inner loops compares the next M elements. If the inner loop completes without finding any differences, then a matching sequence has been found. Extrapolating the time the program takes on a small data set reveals that to run the program on a real data set would take several days.

For I = 1 to (N-M)
..For J = I+1 to (N-M)
….Found = True
….For K = 1 to M
……If X[I+K-1] <> X[J+K-1] Then
……..Found = False
….Next K
….If Found then goto *
..Next J
Next I
*

The same problem solved with APL was much faster. The arguments comparing APL and FORTRAN in the past mirrors in a curious fashion the arguments still heard in comparing Java and C++. APL was an interpreted language, while FORTRAN was compiled. For simple tasks APL had much slower execution, although development time was often much faster. Thus, APL was often dismissed as a toy language, while FORTRAN was the language of choice for "serious" programmers. The APL program approaches the problem from an entirely different perspective. Rather than thinking about the data as a single one-dimensional vector, the data is restructured into a two-dimensional matrix. Each row of this matrix would have M elements. The first row would have the first M values, the second would begin the M values that started at position 2 note the overlap), and so on. After constructing this matrix, the APL program then sorts the matrix by rows. If any sequence is repeated, it would appear as two adjacent rows with the same values. This was quite easy to detect. The biggest surprise, however, lay in execution time. The new program, even though it was interpreted, runs in a fraction of the time required by the other program.

M=6

A  C  T  C  G  G  positions 1 to M
C  T  C  G  G  A  positions 2 to M+1
T  C  G  G  A  T  positions 3 to M+2
C  G  G  A  T  T  positions 4 to M+3
G  G  A  T  T  C  positions 5 to M+4
G  A  T  T  C  T  positions 6 to M+5
 .  .  . 
T  G  G  A  C  C 
G  G  A  C  C  C
 .  .  . 

The difference in that the two languages naturally led the programmer to view the world in a different way. The FORTRAN culture values loops, arrays, and simple and direct programs that can be efficiently compiled. APL is a language designed for manipulating matrices of two or more dimensions. Operations such as sorting are built into the language. Because they are such an intrinsic part of the language, and APL community values finding novel uses for matrix operations. Thus, the APL culture naturally led the programmer to find a solution that in this case happened to be much faster than the solution that naturally occurred to the FORTRAN programmer. The important point to observe is that the FORTRAN language did not preclude a solution like that found by the APL programmer, simply that it would not be natural. The same is true of languages and paradigms in general.

The point of this example is to show the tool you use influences how the job is done. O-O programming is a different tool and you have to think in terms of that tool to solve problems. Thinking in former programming styles can greatly hinder your ability to effectively use and O-O language.

Another example is, think about using a hammer to screw a screw into a piece of wood. It can be done but the thought process is a pounding motion. Whereas, using a screwdriver leads to the thought of a twisting motion to accomplish the task.

Power of the Metaphor

Because the OOP view is similar to the way in which people go about solving problems in real life (finding another agent to do the real work!), intuition, ideas, and understanding from everyday experience can be brought to bear on computing. On the other hand, common sense was seldom useful when computers were viewed in the process-state model, since few people solve their everyday problems using pigeon-holes.

Church’s Conjecture

Any computation for which there exists an effective procedure can be realized by a Turing machine. This is the opposite of the Sapir – Whorf hypothesis. Anything can be done in any language, but it may simply be easier or more efficient to use one language or another. Object-oriented languages are important because they make it easy to think about the world in an object-oriented way. Simply, that thinking in an object-oriented way and working in an object-oriented language go naturally together.

Complexity and Interactions

"The complexity of software is an essential property, not an accidental one" says Frank Brooks in No Silver Bullet. This essential (the complexity may be mastered but not eliminated) property is that "industrial-strength" software is difficult and a single developer can not comprehend all the subtleties of its design. This complexity derives from four elements: the complexity of the problem domain, the difficulty of managing and developmental process, the flexibility possible through software, and the problems of characterizing the behavior of discrete systems (systems with states and the transition from one state to another is not always deterministic). (Booch 2nd Ed, Section 1.1)

People have always tried to use computers to solve problems that were just a little more difficult than they knew how to solve. Therefore, there has been a long tradition of looking for mechanisms to help manage an ever increasing level of complexity. The idea that we have a "software crises" came about at a NATO meeting in 1968. What the term really represented was a realization that the most significant problems in software development were not algorithmic in nature, but were caused by communication difficulties and the management of complexity of large multi-person programming teams.

Many software systems are complex not because they are large, but because they have many interconnections. Even quite large systems can be easily understood if they have a regular structure. But eliminate a regular structure, and the task of understanding becomes much more difficult. Interconnections make it difficult to understand sections of software in isolation, or to carry components from one project to the next. This, in turn, makes it difficult to divide a project between several programmers. To manage complexity we use abstraction, the elimination of details. One definition says that the difference between data and information is the amount of detail you can throw away. By finding a way to hide detail from a programmer, we can help that programmer deal with ever more complex tasks. O-O programming helps to solve these problems. The details of objects can be hidden so that at a given level multiple objects can be stored in the short-term memory of the human brain so that a grasp of the meaning of their interaction can be obtained.

Match Problem Domain with Problem Solving Style

All these concepts help to drive O-O programming to be the best solution that we have at this time to match the real-world problem domains to a problem solving style.

Imperative vs. Object-Oriented

Object-Oriented Programming is often described as a new paradigm. The word paradigm is not one that is commonly encountered in everyday life, so one place to begin is to explore this word in a little more detail. If you look in the dictionary for a definition of paradigm, you are likely to find some like the following.

Paradigm. Noun. A list of all the inflectional forms of a word taken as an illustrative example of the conjugation or declension to which it belongs. An example or model.

That is, a paradigm might be something like an example sentence that you recall to help remember how to conjugate a verb in a foreign language. What in the world does this have to do with computer programming languages? To understand the connection, one has to know that there was a historian of science, Thomas Kuhn, who used to word to describe the progress of scientific thought. His idea was similar to the concept of punctuated equilibrium that has recently become popular in the evolutionary biology community as a "band-aid" for the fundamental wound that is inherent in the theory of evolution. That is, the idea suggests that life progresses with little change for long periods of time. Thus, in a very short period of time, things change radically before settling down again for a long period. Kuhn argued that science changed when our model of the world, our paradigm for describing the universe, was changed. An example paradigm change was the introduction of Newtonian physics, or the overthrow of Newtonian physics by relativity. The word made its way into computer science in the 1979 Turing Award lecture given by Robert Floyd. The Turing award is rather like the Nobel Prize in computer science. The title of his talk was "The paradigms of Programming", and he applied this notion of a mind-set or way of looking at the world to the way in which software gets created. Since that time people have used the term "programming paradigm" to mean a systematic way of thinking about what is happening when software executes. Not the actual low level description of what is really going on, but a high level intuitive model that will assist in the visualization and creation of programs.

Imperative

The imperative (to do) paradigm is the traditional model of computation. In the imperative model we think of the computer as consisting of memory and the CPU, with the state of the computer being given by the contents of memory. Actions occur as a result of assignment, which will change the values of variables. These actions can be placed in loops if we want to do a lot of work. The processing unit is separate from memory, and acts upon memory. This is sometimes called the pigeon-hole model of computation. This is because we think of memory as a series of boxes, perhaps with labels, perhaps just with numeric addresses. The computer moves from instruction to instruction, pulling values out of boxes, acting on them, and pushing them back into boxes. When instructions are all finished, the contents of a specific set of boxes will yield the results we seek. This model is a rather accurate picture of the actual workings of the computer. However, it is not particularly useful. Very few of us solve our problems in real life using pigeon-holes. So we can not use our experience from real life to help us solve problems expressed in this fashion.

Why does it fail?

Data separate from functions. Data is often global. Access to the data is uncontrolled and unpredictable. Testing and debugging is in turn difficult.

Object-Oriented

Imperative follows the Indicative – do something based on who you are.

Alan Kay has said that when he was designing Smalltalk he thought about the idea of recursive design. The idea of recursive design is that the structure of the part mirrors the structure of the larger unit, rather the way that fractals work. That is, rather than think of a computer as being composed of parts that each have less power than the original, what if we think of a computer as being made up of lots of little processing engines. Each of these processing parts, called objects, can be thought of as a computer-in-the-small. They will have similar computational power as the computer as a whole, and the processing will proceed by these things acting together.

How does O-O try to succeed?

O-O combines data and procedures together and controls access (encapsulation). This allows interactions to be able to make fewer assumptions about the context of the data.

An Object:

Data

Method

Method

Method

O-O provides a more disciplined facility to accomplish encapsulation than does the imperative paradigm.

Maps closely to real world. For example, a person is an object – an instance of the human class. A person is asked questions (messages) to get information about that person. The data and behaviors are combined into one object (a person).

O-O Foundations

The Object-Oriented paradigm is based on four fundamental concepts. The most essential are abstraction and encapsulation. Abstraction and encapsulation are also fundamental in good imperative programming. Polymorphism and inheritance are concepts that are essentially new and relatively easy to provide in the O-O paradigm.

Abstraction

The idea of abstraction is to eliminate the details and concentrate on the essential characteristics that describe an entity.

Encapsulation

The idea of encapsulation is to package all aspects and only those aspects of an entity within itself. You enclose traits and behaviors within a container. The container is covered so you can not see how the traits and behaviors actually work. However, you can see those traits and behaviors manifest themselves by the appearance of the container. For example, imagine a person who is very fashion conscious. We do not know exactly why the person is that way but we can see it in the clothes they wear. Their fashion sense may change with the times but we can still see it in the clothes they wear.

Abstraction and Encapsulation give rise to the concept of modularity. Modularity means that systems are partitioned into individual components to reduce complexity and create a number of well-defined, documented boundaries. The balancing act between abstraction and encapsulation when creating modules is accomplished by employing the goal of creating cohesive (by grouping logically related abstractions) and loosely coupled (by minimizing the dependencies among modules) modules.

Polymorphism

The idea of polymorphism is that each entity knows how to do its own behavior. Different entities may have the same type of behavior but they each accomplish the behavior in their own way. For example, two persons have the behavior of what they think about the clothes they wear. One person may be very fashion oriented and thus dresses accordingly. Another person has the same behavior of dressing but their fashion sense may be very utilitarian so they wear a plain uniform.

Inheritance

The idea of inheritance is that an entity can inherit, extend, override and add traits and behaviors from another entity. This inheritance relationship is classically expressed in terms of the parent/child relationship. To inherit means that the child also has the same traits and behaviors as the parent. To extend is to inherit and add to the trait or behavior. To override is to completely change the inherited trait or behavior and to add is to express new traits and behaviors not present in the parent. For example, a child could "inherit" (in a nurture sense) a parents fashion sense. That child could also extend, override and add to that fashion sense.

Summary

These concepts can be remembered as A PIE.

Object-Oriented Paradigm Hierarchy Diagram:

Objects | Classes | Attributes | Methods | Messages | Instances

Interface | Composition | <Overloading>

Polymorphism | Inheritance

Encapsulation | Abstraction

Java and OO: http://java.sun.com/docs/white/langenv/
Section 3 is another source for introductory material.

Examples

Let’s explore some examples that compare and contrast imperative programming with
O-O programming. The first example considers providing logic that will provide that sum of two numbers. To implement this with imperative programming we could simply write a function that sums two numbers.

Function int sum(int a, int b)
{
...return (a + b);
}

With O-O programming we could create a class called Math and provide a sum method.

Class Math
{
..Method int sum(int a, int b)
..{
….return (a + b);
..}
}

This examples shows no difference between the two paradigms. The syntax of course is different. If this is all there was to O-O then it would not be very useful. The reason why this example does not show the power of O-O is because there is no state information that is maintained between calls to the sum operation.

The second example is to control a heater. The heater can be turned off or on and its state can be checked. In O-O we could write the following code.

Class Heater
{
..boolean heater_state;
..Method TurnOn()
..{ …
….heater_state = true; //true = on
..}
..Method TurnOff()
..{ …
….heater_state = false; //false = off
..}
..Method IsOn()
..{
….return (heater_state);
..}
}

Program
{
..Heater1 = new Heater();
..Heater1.TurnOn();
..Heater1.TurnOff();
..Print("Is heater on? "+Heater1.IsOn());
}

Imperative programming could implement this as

Procedure TurnOn()
Procedure TurnOff()
Function IsOn()

Main program procedure
{
..Boolean heaterstate;
..TurnOn();
..heaterstate = false; //uncontrolled access
..IsOn();
}

Notice that the imperative program has a global variable to record the state of the heater. This allows for uncontrolled access and unpredictable behavior. The reason that we have a global variable is that state information between each operation needs to be maintained and the facilities for organizing code with imperative programming does not give an alternative. Another problem not shown here but can quickly arise is the need for multiple heaters. With imperative programming we need an array of heater state variables. With O-O programming we create more heater objects. Granted the heater objects are probably kept in an array but all the other information is handled because of the O-O paradigm for organizing code.

O-O makes it easier to practice the discipline needed to write good, robust code.

O-O Concepts

To fully construct an Object-Orient system based on the O-O foundation other concepts are needed. Many of these concepts are abstract constructions that make the foundation useful in an Object-Oriented system (analysis, design and programming language).

Objects

An object has state, exhibits some well-defined behavior, and has a unique identity.

State – all the static properties and current dynamic values of properties within an object.

Behavior – how an object acts and reacts to changes in its state through messages passed to it.

Identity – The property that distinguishes an object from all other objects.

Attributes & methods

An object is the packaging of the logically connected data and behaviors that describes an entity. An object should model its real-world counterpart and contain only the data and behavior necessary to model the object. In O-O parlance, data and behaviors are known as attributes and methods.

Message-passing

Objects interact with each other by passing messages to one another. These messages must conform to the interface that an object provides. In OOP things happen whenever messages are sent between objects.

Element of code organization

In procedural programming parlance there is no one element that is analogous to an object - a subroutine and all the data associated with it would have to be packaged together to constitute an object. This is difficult to accomplish because of the way in which program elements are organized in the imperative paradigm. In O-O programming the structure of programs are based upon an organization of objects.

Instances

Objects are unique, unique from other classes and from other objects of the same class. Because attributes and methods are combined in an object, a different instance of the object must be constructed for each different object of the same type that is needed. For example, if we have a car object and one of its attributes is color then if I have two cars then I need two objects so that the color of each car can be independently maintained. Conceptually each object instance has a copy of its own attributes (variables) and methods (code). However in the implementation of an O-O language the attributes are separate but the methods are a common code base. This is just an efficiency detail.

Life-cycle

An object goes through a life-cycle. First it is "born" (instantiated). Second, it "lives" – this particular instance of the object can perform behaviors and reveal and set data about itself. Lastly, an instance of an object may "die" – it no longer exists.

An object is instantiated with what is known as a constructor. A constructor is called when you want an instance of an object to be created. The constructor initializes the instance of the object into a known state – variables are initialized.

An object is destroyed with what is known as a destructor if the object must be explicitly destroyed. However, in Java the destruction of object is done implicitly during "garbage collection". In other words, the Java environment keeps track of when an object is longer needed and the memory the object is occupying is cleaned up at the discretion of the Java environment.

Classes

A class and an object are represented in the same source code component. However there is a difference. A class is the "definition" of the object, an object template. This is analogous to a cookie cutter and cookies. A cookie is an object, cookies are instances of objects and the cookie cutter is a class. Sometimes these terms are used interchangeably yet that is not quite proper. The root of the confusion and sloppy usage stems from the word object is used in a generic and specific sense. When we talk about the Object-Oriented paradigm we use the word object in a generic sense to mean a definition (class) and an instance (object). When we talk about the specific details of an Object-Oriented language or program we often use the word object in a specific sense to mean an instance of an object. The distinction that is important is that we define a class so that we can create one or more instances of an object. Each instance of the object is also referred to as an object.

Instances

An instance is a variable of the object type whose value is a reference to a particular object of the declared object type.

Ex. An integer is an object. Variable countA of type integer contains the value of the integer. If we need another counter then another variable countB of type integer is declared.

When multiple different objects of the same type are needed a variable is declared of the object type and that variable contains the "value" of the object. Each of these different variables is an instance of the object.

Attributes

An attribute is a piece of data that contains a value that helps to describe an object. Another terms for this is a property. In procedural programming an analogous element would be a variable. However, there is a slight difference. In O-O, an attribute is a variable that describes a characteristic about the object. A variable within an object could be there to just help perform the logic of a method but not really describe a characteristic of the object.

Methods

A method is the programming logic that implements the behaviors of an object. A method is analogous to a procedure, function or routine in a procedural programming language. In contrast to the distinction made between attributes and variable in an object, methods do not have as clear a distinction. Some methods implements behaviors of an object and others contain code to help support the first type of methods. This is really done so that routines can be consolidated in an object much as would be done in a procedural program.

Constructors

A special type of method is a constructor. A constructor is the routine called whenever a new instance of an object is created ("constructed"). Constructors should contain logic to place the created object in a good state in terms of the initial attribute values and any behaviors that need to be done.

Messages

Messages are analogous to a function call, return value pair. To communicate with objects a message is passed (like a function call) and if the method returns a value it is done through a message back to the calling object.

Interface (generic sense)

The interface to a class describes how to use the class. This is done by creating methods that messages can be sent to, to invoke the object to perform some behavior. In other words, the interface is the method signatures and return values of the class. The interface should contain the minimum set of behaviors that fully models the object. The interface should also be fairly abstract so that the object can be reused in other applications. The interface is different from the implementation of the class. The interface should be designed so that it does not change over time whereas the implementation of accomplishing the behaviors of the object may change. The goal is to be able to change the implementation of an object without changing the programs that use the object.

Refer to the electrical outlet example to realize the difference between interface and implementation, an abstract interface, and an object having different users who must conform to the interface.

Abstraction

Abstraction is a method that humans use to simplify complex concepts so that more pieces of a system can be held in short-term memory of the brain at once. When trying to understand a complex system there are parts of it at various times that only needs to be thought of at a very high level. This is the process of abstraction, the details are ignored and only the essential characteristics are thought of at that time.

An example of this in the stereo system is the speakers. We dealt with them at the level of producing sound. We did not go into all the details of how the speakers are constructed and how sound is produced from their design.

Encapsulation

Encapsulation is a packaging technique that goes hand in hand with abstraction. In the O-O world data and logic is packaged together to model an object. The details of how the object actually does its operations are hidden. An interface is provided so that the object can be correctly used.

Abstraction concentrates on the most important or essential aspects while suppressing or ignoring less important, immaterial, or diversionary details. Developers abstract to view problems with varying degress of detail depending on current context and needs. Developers abstract to manage complexity, promote correctness, extensibility,

maintainability, resuability, understandability.

Abstraction (simplify, remove non-essential details, reduce the object to a functional minimum, construct a generic interface) is concerned with breaking a problem up into appropriate pieces during the design.

Encapsulation (containing and hiding information and behaviors) continues the abstraction concept and is implemented with the wrappering technique.

Overloading and Overriding

Overloading is the concept of applying multiple meanings to an operation with the same name. The operation name can be a method name, known as method overloading, and an operator symbol, known as operator overloading.

With method overloading, the same method name is used but the other parts of the method signature (argument lists) are different. Depending on the types, number of and order of arguments passed to the method name, the actual method name and argument list that matches the calling specification will be selected to be called. This reveals that the method to be called is selected by more than just the method name, it also includes the argument list. For example, a method named print may have several implementations, each with a different argument list. The arguments would be different for each type of data that could be printed, a string, integer or boolean value. Thus, the print method would have three different signatures:

Print(String s)
Print(int a)
Print(boolean b)

When the print method is called the method that has the matching argument type would be executed.

With operator overloading, the meaning of the operator is determined by the type(s) of operands that it works upon. For example, the plus (+) sign can perform an addition if the operands are numbers and a concatenation if the operands are strings.

Overriding applies to methods in an inheritance relationship. A child object can override the method implementation of a parent object by specifying the same method signature (method name and argument list) in the child object.

Polymorphism

Polymorphism means that the behavior an object exhibits changes depending on the type of object and the operands it employs.

Polymorphism can be accomplished by using the same method name with different arguments, this is known as parametric polymorphism. Method overloading discussed above facilitates this type of polymorphism. The classic example of the print method accepting different argument type is also present above.

Polymorphism is also accomplished by method overriding - different objects have the same method name and argument list but implement a different behavior. The classical example is that of a shape class that contains multiple subclasses. The subclasses are actual shapes like circle, box and triangle. The logic necessary to draw these shapes is different but the name of the operation could be simply Draw. Depending on what type of shape is being referenced the corresponding draw operation is used. This is known as method overriding.

Polymorphism is further accomplished by passing different objects that have a common parent object to a method and depending on the specific child object that is passed, specific logic is performed. A classic example of this is to pass a menu item command to a method which then calls a pre-defined method name that is the same in all menu item command object, but each menu item command object does different logic within that pre-defined method. This makes it easy to add menu items without changing the code that uses the menu item command objects. Only the new menu item command object has to be coded in accordance with defined specifications.

Polymorphism contributes to the O-O paradigm by supporting the principles that an object should be able to answer all the important questions about itself and an object should be responsible for itself. Great power can be derived from thinking about how object interactions can take place in a polymorphic fashion.

Here is an example from "The Object-Oriented Thought Process" – Weisfeld, p.141-146.

public abstract class Shape
{
	public abstract void draw();
}

public class Circle extends Shape
{
	public void draw()
	{
		System.out.println("I'm a circle");
	}
}

public class Rectangle extends Shape
{
	public void draw()
	{
		System.out.println("I'm a rectangle");
	}
}

public class Triangle extends Shape
{
	public void draw()
	{
		System.out.println("I'm a triangle");
	}
}

public class TestShape
{
	public static void main(String args[])
{
		Circle c1 = new Circle();
		Rectangle r1 = new Rectangle();
		Triangle t1 = new Triangle();

		c1.draw();
		r1.draw();
		t1.draw();
	}
}

can refine the TestShape class with:  (shows the menu type example of polymorphism)

public class TestShape
{
	public static void main(String args[])
{
		Circle c1 = new Circle();
		Rectangle r1 = new Rectangle();
		Triangle t1 = new Triangle();

		drawMe(c1);
		drawMe(r1);
		drawMe(t1);
	}

	static void drawMe(Shape s)
	{
		s.draw();
	}
}

Double Dispatch

Suppose we have a draw method that based on the particular subclass of a shape object (rectangle, triangle, circle) it will draw the appropriate image. This is single polymorphism. Now suppose we want to be able to draw those images in two different modes, slow but hi-resolution (graphic) and fast but low-resolution (text). We could have a DrawGraphic and DrawText method in each subclass but this doesn't scale well. What we want is to pass an object argument that specifies the display device type to the draw method. The draw(displaydevice) method would draw the appropriate object on the appropriate device. This is multiple polymorphism. The first level of polymorphism is on the object's subclass of shape and the second level is on the argument's subclass of displaydevice. This can be extended to any degree of polymorphic dispatch.

Inheritance

Inheritance is represented in the classic parent-child relationship. All inheritance relationships are in the form of an "Is-A" relationship. Bobby is a child of Mary. The power of inheritance is in its abstraction and organization techniques. Inheritance forms a hierarchy of general to specific objects. Inheritance defines a strict relationship between classes’. This facilitates code reuse and better design by organizing classes and factoring out commonalties. A broad knowledge of the world is very helpful do to good inheritance modeling - designing accurate models. However, the more accurate the model, the more objects that will be needed and thus the complexity of the system increases. Therefore, good design requires a balanced use of the different O-O foundations available. Inheritance was originally all the rage in O-O, now it has been observed that composition is possibly more important. Judicious and correct use of the O-O components is what is best in designing O-O systems.

Composition

Composition has its basis in a referential relationship. These relationships are all in the form of a "Has-A" relation. A car has a steering wheel. Complex objects can be built by joining objects together. These complex objects can be thought of as subsystems, which in turn can be used to simplify the description of a system. Composition depends on making objects interchangeable. When this is done systems and subsystems can be built and tested independently. This yields robust, reusable code. There are several different ways in which objects can be composed. The distinction is based on the subtle ways in which objects can refer to one another and also the place where granularity is sacrificed.

Aggregation

Aggregation represents a whole-part relationship. This is normally viewed from the perspective of the whole. A complex object composed of other objects.

Aggregation example, A TV is the whole, the tuner, picture tube, speakers, etc… are the parts. If we are viewing the TV object as a whole, it is an aggregation of the parts.

Link

Link represents a peer-to-peer relationship. Both objects are equal, they are joined together to cooperate in the accomplishment of some task. A unidirectional link relationship is known as a "using" relationship, like client/server for a particular service.

Association

Association represents a combination of the aggregation and link relationship. An association can have the form of a one-to-one, one-to-many or many-to-many relationship. It is normally viewed from the perspective of the parts that make up a whole. This association can be expressed in the idea that one object wants another object to perform a service for it.

Association example, A whole computer system may be composed of several parts, a PC, mouse, keyboard, monitor and printer. Each of the parts is a whole system itself but are associated together from the perspective of a set of parts making another whole and these parts each provide a set of services to the other parts.

The place where the lines are drawn to describe the type of composition that is being represented is in the balance between object granularity and complexity.

Framework

A framework is a program structure provided for a programmer to build an application. This lets the programmers concentrate on details and gives them a good overall system to work within. In contrast, a library provides functions or classes, which are used in sequences, and combinations the programmer feels are appropriate. A framework inverts the control provided by libraries. The framework determines the sequence and combinations of how objects are used. The programmer customizes the objects and behaviors they will exhibit when placed within the framework. The framework actually limits a programmer to work within the system that the framework designer created. Creating frameworks is a very difficult thing to construct well.

Dynamic Binding

Any binding that takes place at run-time after the message is received due to compile-time ambiguities caused by inheritance and polymorphism. Dynamic binding allows the system to dynamically decide what method to use, based on the type of the current object.

Persistence

The ability of an object to continue to exist after the execution of a program, process, or thread that created it. Most simply this is understood by thinking that an instance of an object is saved to a disk file.

OOP Recap

Kay’s Description of Object-Oriented Programming

Object oriented programming is based on the principle of recursive design.

  1. Everything is an object.
  2. Objects perform computation by making requests of each other through the passing of messages.
  3. Every object has it's own memory, which consists of other objects.
  4. Every object is an instance of a class. A class groups similar objects.
  5. The class is the repository for behavior associated with an object.
  6. Classes are organized into singly-rooted tree structure, called an inheritance hierarchy.

Elements of OOP - Objects

1. Everything is an object.

Our first principle is that everything in our universe is viewed as an object. Rather than thinking about a single memory cell as being our fundamental unit of computation, let us imagine a little computing engine as our basic unit. These little computing engines are called objects in OOP and they perform actions.

Elements of OOP - Messages

2. Objects perform computation by making requests of each other through the passing of messages.

Objects must interact to get anything accomplished. To do this interaction object make requests of one another. These requests are called messages. An object may accept a message, and in return will perform an action and return a value.

Information Hiding

The use of messages to cause objects to interact with one another naturally suggests the concept of information hiding. I, as a user of a service being provided by an object, need only know the name of the messages that the object will accept. The message is simply a request, and does not contain any explicit direction as to how the request is to be honored. I do not need to know how the actions performed in response to my request. Having accepted a message, an object is responsible for carrying it out.

In real life this idea may seem obvious, but it turns out to be a surprisingly important key in controlling the complexity of large software systems. Large systems can be designed as a collection of interacting agents, each providing a service to the others. If the principles of information hiding are observed, then these services can be developed (say, by different members of the programming team) in isolation from each other.

Elements of OOP - Receivers

A message can be contrasted with a function call in a conventional programming language. Both mechanisms are a means for abstracting, encapsulating, and naming a desired behavior. However, messages and function calls differ in two very important respects. A function exists on its own. It may take values as arguments, but the function can be thought of as an independent entity. A message, on the other hand, is always given to a receiver. Thus, there is an object that is accepting responsibility for the message. Furthermore, and more importantly, the interpretation of the message may be different, depending upon the receiver. The same message given to two different receivers may elicit two different types of responses.

Behavior and Interpretation

Although different objects may accept the same message, the actions (behavior) the object will perform will likely be different. The determination of what behavior to perform may be made at run-time, a form of late binding. The fact that the same name can mean two entirely different operations is one form of polymorphism.

In computer science we use the term binding time to refer to the time in the life of a program when a name is tied, or bound, to an attribute. Times range from compile time, very early in the life of a program, to various phases of run-time, very late in the life of a program. In a conventional language, a function and its behavior, that is, its code, is bound very early, at compile time. In an object oriented language the binding of the name, the message, to the code, the behavior is performed very late, at run-time. By permitting different objects to respond to the same message in a variety of ways, this late binding will turn out to be the key to much of the power of the object-oriented technique.

Elements of OOP – Recursive Design

3. Every object has it's own memory, which consists of other objects.

Each object is like a miniature computer itself - a specialized processor performing a specific task. Like the computer-in-the-small that we imagine it is, an object has its own memory, which (following the principle of recursive design) will consist of other objects.

Non-Interference

Tied to the notion of information hiding is an important principle of non-interference. Programs should manipulate information in a precisely defined manner. OOP goes a long way to imposing the discipline to accomplish this. Imperative programming lacks facilities to ensure that this discipline is maintained, yielding the degeneration into uncontrolled and unpredictable access to data.

It is curious that the object-oriented outlook tends to elicit a variety of anthropomorphic descriptions from people. Dan Ingalls, one of the developers of the original Smalltalk systems at Xerox Parc, has described object-oriented programming this way: "Instead of a bit-grinding processor raping and plundering data structures, we have a universe of well-behaved objects that courteously ask each other to carry out their various desires". Another description echoes a quote from a famous presidents inaugural address: "Ask not what you can do TO your data structures, but ask what your data structures can do FOR you".

Once again, this concept of thinking in terms of objects and the services they provide is an important key in structuring large complex software systems. Components in such a system will be characterized by the service they provide, and not by any internal implementation details. This viewpoint permits several programmers to work on the same system with minimal interference in each other’s work. It also makes it much easier to carry significant portions of a program from one application to the next.

Elements of OOP – Classes

4. Every object is an instance of a class. A class groups similar objects.

5. The class is the repository for behavior associated with an object.

A universe that consists of a large number of objects turns out to be rather unwieldy unless we impose some form of structure. We can add structure by saying that every object is an instance of a class. A class is similarly just a grouping of similar objects. In particular, we will say that the class is the repository for behavior associated with an object. By that we mean that all instances of the same class will have the same behavior. Behavior is associated with classes, not with individual instances. All objects that are instances of a class use the same method in response to similar messages.

Elements of OOP - Inheritance

6. Classes are organized into a singly-rooted tree structure, called an inheritance hierarchy.

Thus, our sixth principle of object-oriented programming is that classes can be organized into singly-rooted tree structure, called an inheritance hierarchy. The benefit of doing this is that information, both data and behavior, that I associate with one level of abstraction in this class hierarchy is automatically applicable to lower levels of the hierarchy.

This will turn out to be both a great organizational aid, as well as a great tool for reducing the amount of code that must be written for new applications.

Elements of OOP – Overloading/Overriding

Subclasses can alter or override information inherited from parent classes.

[Exercise: O-O Introduction Exercise]

 

O-O Design

Why start with design?

The answer is that object-oriented programming involves object-oriented thinking, and object-oriented thinking begins with object-oriented design. Furthermore, many object-oriented techniques are difficult to motivate with small toy programs, but take on critical importance when you move on to consider larger software development efforts. By focusing on design we can discuss much larger software systems than we could consider at this point if we limited ourselves simply to code. It is difficult to explain the importance of some object-oriented techniques without an understanding of the problems they are designed to solve. Concentrating on design helps us understand some of the problems involved in programming in the large, without actually doing programming in the large.

Programming in the large is where a team of programmers develops large software applications. In this environment, the major problems are not algorithmic. Instead, the major problems are communication issues. How much information does programmer A need to know about the work being performed by programmer B in order to do his or her job?

Refer to Booch 2nd Ed, Section 1.4 for more information about design importance.

Object-Oriented Analysis (OOA)
Object-oriented analysis is a method of analysis that examines requirements from the perspective of the classes and objects found in the vocabulary of the problem domain.

Object-Oriented Design (OOD)
Object-oriented design is a method of design encompassing the process of object-oriented decomposition. The central problem in OOD is deciding upon the right set of abstractions for a given problem domain. (Refer to Booch 2nd Ed, Chapter 4)

Design is an incremental and iterative process. Yet we want to get as close to having a correct design as we can on the first try. Therefore we can use five meaningful metrics to evaluate the quality of the abstractions in the design.

Coupling – the measure of the strength of association established by a connection from one module to another. Strong coupling complicates a system since a module is harder to understand, change, or correct by itself if it is highly interrelated with other modules. Complexity can be reduced by designing systems with the weakest possible coupling between modules.

Cohesion – measures the degree of connectivity among elements of a single module. Unity of purpose.

Sufficiency – captures enough characteristics of the abstraction to permit meaningful and efficient interaction. This is the minimal interface to the object.

Completeness – The interface to the object captures all of the meaningful characteristics of the abstraction. This is a subjective matter and can be overdone.

Primitiveness – those operations that can be efficiently implemented only if given access to the underlying representation of the abstraction.

(See Booch 2nd Ed, Section 3.6 and Chapter 4)

System design

There are many different methodologies to generate a system design. Learning and evaluating them is beyond the scope of this section. However, it is important to have a methodology because good Object-Oriented programming is so dependent on good analysis and design. Whatever methodology is employed should be one that is usable and actually used.

Some very elementary thoughts on elements of a particular methodology and their usefulness are presented below:

1. Use CRC cards to identify classes and responsibilities.

2. Use use-case scenarios to discover collaborations.

3. Use UML diagrams to document the implementation.

4. Prototype the Graphical User Interface.

While the main emphasis of the design of an application system concentrates on the application logic there are a number of support systems that should be incorporated into the design. It is best to incorporate these systems early into the design so that they can be "integrated" as opposed to "bolted on".

These support systems include such things as:

1. Logging/Auditing – recording what is done by whom in the application.

2. History – a system to archive data.

3. Security – a system to grant and deny access to elements in the application to particular users. The level of granularity is a very important decision to make early in a application systems design.

4. Error Handling – a system for recording, reporting and handling error conditions.

5. Import/Export – a system for exchanging application data.

6. Non-graphical application interface – a system to manipulate the application without being tied to a particular graphical interface. This is usually done in the form of a command line interface and facilitates bulk operations that can be automated.

Object Design

While there is no one-way to design objects there are some guidelines that have been developed to aid in object design such that the objects can be useful. When you design objects remember that you are trying to model objects that equate to the real-world. This means that you should think in terms of encapsulating both data related to the object and behaviors related to the object. If you only include behaviors then you have nothing more than a set of subroutines. The concept of subroutines is good but objects are much more than that. An object is a logical component then encompasses all aspects of the object, namely data and behavior. The thinking needed to design objects this way is opposite of the top-down structured approach with data and behavior separate. The top-down algorithmic decomposition approach leads to a hierarchy of all the steps needed. The object-oriented decomposition approach leads to a model of the key abstractions related to the problem domain. (Booch 2nd Ed, Section 1.3)

Guidelines:

  1. Keep the public interface to a minimum. The class is to provide something useful and concise. The interface of a well-defined object describes the services that the client wants accomplished.
  2. Hide the implementation. The user needs to be involved with the public interface but the implementation should not involve the user. A change in the implementation should not necessitate a change in the application code. All fields private, only accessed via methods.
  3. Create robust constructors. The constructor should put the object into a safe state. This includes attribute initialization and memory management. Provide a default constructor.
  4. Deallocate resources appropriately. Java does garbage collection of memory, but you should make sure files are closed, etc…
  5. Design error handling into the class. The rule of thumb is that an application should never crash. When an error is encountered, the system should either fix itself and continue, or exit gracefully without losing any data that’s important to the user.
  6. Document the class using comments. Comment the interface in terms of how, when and why to use it. Comment the implementation in terms of the "why" of the logic used. Don’t comment code that just restates what the statement does. A developer should be able to read the statement of code to know what it does. You need to comment why the statement is doing what is does and how this fits into the logic of the entire implementation to solve a problem.
  7. Build objects with the intent of cooperating with other objects. If an object is standalone, doesn’t interact with any other object, then there is probably no reason to build the object. Object should provide services to convey information to other objects.
  8. Objects should be designed with reuse in mind. Code should be written with reuse in mind. This is not a trivial task it takes discipline and practice. Don’t make an object that can only be used by one system.
  9. Design with extensibility in mind. Remember that an object can be extended via inheritance. Don’t design a class such that it can’t be logically extended. For example, don’t create a person class that has behavior that doesn’t allow it to be extended to an employee or vendor object. This deals with using the proper level of abstraction for the object. A person object should contain only the data and behaviors that are specific to a person, and all persons not a subclass of persons.
  10. Follow a naming convention and use descriptive names for classes, attributes and methods.
  11. Abstract out non-portable code using a wrapper object. Non-portable code should be put in it’s own object and provide a public interface to provide the information and services necessary. Your primary object then uses the wrapper class. This will make it easier to change the primary class if the non-portable object changes or is done with a different object. If the interface is good then even the primary objects code won’t have to change at all.
  12. Provide a way to copy and compare the object. You should understand how the particular language deals with these operations and provide services as appropriate.
  13. Keep the scope small. This is related to abstraction and hiding the implementation.
  14. A class should be responsible for itself. Pg. 90-91. Fig 5.6,5.7.
  15. Design with maintenance in mind. Designing useful and concise classes promotes this. Organize your code into manageable pieces. Reduce interdependent code – that is, changes in one class have no or minimal effects on other classes. Avoid highly-coupled classes (dependent on one another). Don’t change the public interface.
  16. Design testing in the class with stubs and use of main().

Development Process

Use iteration

Test the interface with stubs

Tools

UML – Unified Modeling Language. Many books available.

[Index]